/******************************************************************************
 * Copyright (c) 2022 Telink Semiconductor (Shanghai) Co., Ltd. ("TELINK")
 * All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *****************************************************************************/

#ifndef DRIVERS_B91_DRIVER_EXT_EXT_PM_H_
#define DRIVERS_B91_DRIVER_EXT_EXT_PM_H_

#include "../pm.h"
#include "types.h"

#ifndef PM_32k_RC_CALIBRATION_ALGORITHM_EN
#define PM_32k_RC_CALIBRATION_ALGORITHM_EN 1
#endif

/**
 * @brief	available wake-up source for customer
 */
typedef enum {
    // not available wake-up source for customer
    PM_TIM_RECOVER_START = BIT(14),
    PM_TIM_RECOVER_END = BIT(15),
} pm_tim_recover_wakeup_src_e;

typedef pm_sleep_mode_e SleepMode_TypeDef;
typedef pm_sleep_wakeup_src_e SleepWakeupSrc_TypeDef;

typedef int (*suspend_handler_t)(void);
typedef void (*check_32k_clk_handler_t)(void);
typedef unsigned int (*pm_get_32k_clk_handler_t)(void);
typedef int (*cpu_pm_handler_t)(SleepMode_TypeDef sleep_mode, SleepWakeupSrc_TypeDef wakeup_src,
                                unsigned int wakeup_tick);
typedef unsigned int (*pm_tim_recover_handler_t)(unsigned int);

extern cpu_pm_handler_t cpu_sleep_wakeup;
extern suspend_handler_t func_before_suspend;
extern check_32k_clk_handler_t pm_check_32k_clk_stable;
extern pm_get_32k_clk_handler_t pm_get_32k_tick;
extern pm_tim_recover_handler_t pm_tim_recover;

/**
 * @brief	gpio wakeup level definition
 */
typedef enum {
    Level_Low = 0,
    Level_High = 1,
} pm_gpio_wakeup_Level_e;

/**
 * @brief   deepsleep wakeup by external xtal
 */
typedef struct {
    unsigned char ext_cap_en;  // 24xtal  cap
    unsigned char pad32k_en;
    unsigned char pm_enter_en;
    unsigned char rsvd;
} misc_para_t;

extern _attribute_aligned_(4) misc_para_t blt_miscParam;

#define SYS_NEED_REINIT_EXT32K   BIT(1)
#define WAKEUP_STATUS_TIMER_CORE (WAKEUP_STATUS_TIMER | WAKEUP_STATUS_CORE)
#define WAKEUP_STATUS_TIMER_PAD  (WAKEUP_STATUS_TIMER | WAKEUP_STATUS_PAD)

void bls_pm_registerFuncBeforeSuspend(suspend_handler_t func);

/**
 * @brief analog register below can store infomation when MCU in deepsleep mode
 * 	      store your information in these ana_regs before deepsleep by calling analog_write function
 * 	      when MCU wakeup from deepsleep, read the information by by calling analog_read function
 * 	      Reset these analog registers only by power cycle
 */
#define DEEP_ANA_REG0 0x39  // initial value =0x00
#define DEEP_ANA_REG1 0x3a  // initial value =0x00
#define DEEP_ANA_REG2 0x3b  // initial value =0x00
#define DEEP_ANA_REG3 0x3c  // initial value =0x00
#define DEEP_ANA_REG4 0x3d  // initial value =0x00
#define DEEP_ANA_REG5 0x3e  // initial value =0x00
#define DEEP_ANA_REG6 0x3f  // initial value =0x0f

/**
 * @brief these analog register can store data in deepsleep mode or deepsleep with SRAM retention mode.
 * 	      Reset these analog registers by watchdog, chip reset, RESET Pin, power cycle
 */

#define DEEP_ANA_REG7 0x38  // initial value =0xff

// ana3e system used, user can not use
#define SYS_DEEP_ANA_REG PM_ANA_REG_POWER_ON_CLR_BUF0

/**
 * @brief      This function serves to set the working mode of MCU based on 32k crystal,e.g. suspend mode,
 *             deepsleep mode, deepsleep with SRAM retention mode and shutdown mode.
 * @param[in]  sleep_mode - sleep mode type select.
 * @param[in]  wakeup_src - wake up source select.
 * @param[in]  wakeup_tick - the time of short sleep, which means MCU can sleep for less than 5 minutes.
 * @return     indicate whether the cpu is wake up successful.
 */
int cpu_sleep_wakeup_32k_rc(SleepMode_TypeDef sleep_mode, SleepWakeupSrc_TypeDef wakeup_src, unsigned int wakeup_tick);

/**
 * @brief      This function serves to set the working mode of MCU based on 32k crystal,e.g.
 *             suspend mode, deepsleep mode, deepsleep with SRAM retention mode and shutdown mode.
 * @param[in]  sleep_mode - sleep mode type select.
 * @param[in]  wakeup_src - wake up source select.
 * @param[in]  wakeup_tick - the time of short sleep, which means MCU can sleep for less than 5 minutes.
 * @return     indicate whether the cpu is wake up successful.
 */
int cpu_sleep_wakeup_32k_xtal(SleepMode_TypeDef sleep_mode, SleepWakeupSrc_TypeDef wakeup_src,
                              unsigned int wakeup_tick);

void pm_sleep_start(void);

/**
 * @brief   This function serves to reboot chip.
 * @param   none.
 * @return  none.
 */

void start_reboot(void);

/**
 * @brief   This function serves to recover system timer from tick of internal 32k RC.
 * @param   none.
 * @return  none.
 */
unsigned int pm_tim_recover_32k_rc(unsigned int now_tick_32k);

/**
 * @brief   This function serves to recover system timer from tick of external 32k crystal.
 * @param   none.
 * @return  none.
 */
unsigned int pm_tim_recover_32k_xtal(unsigned int now_tick_32k);

/**
 * @brief   This function serves to get the 32k tick.
 * @param   none
 * @return  tick of 32k .
 */
extern unsigned int get_32k_tick(void);

unsigned int clock_get_digital_32k_tick(void);

/**
 * @brief      This function serves to determine whether wake up source is internal 32k RC.
 * @param[in]  none.
 * @return     none.
 */
static inline void blc_pm_select_internal_32k_crystal(void)
{
    cpu_sleep_wakeup = cpu_sleep_wakeup_32k_rc;
    pm_tim_recover = pm_tim_recover_32k_rc;

    blt_miscParam.pm_enter_en = 1;  // allow enter pm, 32k rc does not need to wait for 32k clk to be stable
}

extern void check_32k_clk_stable(void);

/**
 * @brief      This function serves to determine whether wake up source is external 32k RC.
 * @param[in]  none.
 * @return     none.
 */
static inline void blc_pm_select_external_32k_crystal(void)
{
    cpu_sleep_wakeup = cpu_sleep_wakeup_32k_xtal;
    pm_check_32k_clk_stable = check_32k_clk_stable;
    pm_tim_recover = pm_tim_recover_32k_xtal;
    pm_get_32k_tick = get_32k_tick;
    blt_miscParam.pad32k_en = 1;  // set '1': 32k clk src use external 32k crystal
}

/**
 * @brief      This function servers to wake up the cpu from sleep mode.
 * @param[in]  sleep_mode - sleep mode type select.
 * @param[in]  wakeup_src - wake up source select.
 * @param[in]  wakeup_tick - the time of sleep,unit is 31.25us,1ms = 32.
 * @return     indicate whether the cpu is wake up successful.
 */
int cpu_long_sleep_wakeup_32k_rc(SleepMode_TypeDef sleep_mode, SleepWakeupSrc_TypeDef wakeup_src,
                                 unsigned int wakeup_tick);

/**
 * @brief      This function serves to determine whether mcu is waked up from deep retention.
 * @param[in]  none.
 * @return     1- yes , 0- no.
 */
static inline int pm_is_MCU_deepRetentionWakeup(void)
{
    return (g_pm_status_info.mcu_status & MCU_STATUS_DEEPRET_BACK);
}

/**
 * @brief      This function serves to determine whether mcu is waked up by pad.
 * @param[in]  none.
 * @return     1- yes , 0- no.
 */
static inline int pm_is_deepPadWakeup(void)
{
    return g_pm_status_info.is_pad_wakeup;
}

/**
 * @brief      This function serves to get the status of mcu.
 * @param[in]  none.
 * @return     mcu_status.
 */
static inline int pm_get_mcu_status(void)
{
    return g_pm_status_info.mcu_status;
}

#define cpu_set_gpio_wakeup pm_set_gpio_wakeup

static inline unsigned int pm_get_latest_offset_cal_time(void)
{
    return pmcd.offset_cal_tick;
}

/**********************************  Internal APIs (not for user)***************************************************/
extern unsigned char tl_24mrc_cal;
extern unsigned int g_pm_tick_32k_calib;
extern unsigned int g_pm_tick_cur;
extern unsigned int g_pm_tick_32k_cur;
extern unsigned char g_pm_long_suspend;
extern unsigned int g_pm_multi_addr;

extern unsigned int g_sleep_32k_rc_cnt;
extern unsigned int g_sleep_stimer_tick;

extern unsigned int ota_program_bootAddr;
extern unsigned int ota_program_offset;

#define PM_MIN_SLEEP_US 1500  // eagle

#endif /* DRIVERS_B91_DRIVER_EXT_EXT_PM_H_ */
